//=============================================================================
///	Symbol
//=============================================================================

#include "ssym.h"
#include "smem.h"
#include "sutil.h"
#include "sstream.h"
#include "seval.h"		// for push_stack, pop_stack
#include <string.h>
#include <assert.h>

#define	SymbolHashBufSize	((1<<9)-1)


static void sym_write(SExp s, SExp strm, int escape) {
	Symbol* p = &s.ptr->symbol;
	write_cstr(p->str, strm);
}

static STypeInfo sym_typeinfo = {
	tSymbol,	0,
	"sym",
	NULL,
	NULL,
	NULL,
	sym_write,
	NULL,
	NULL,
};

static Symbol* new_symbol(const char* str) {
	int l = strlen(str);
	Symbol* p = smalloc(sizeof(*p) + l);		// no need +1 for '\0'
	p->typeinfo = &sym_typeinfo;
	strcpy(p->str, str);
	return p;
}


// mark hash table
static void ht_mark(HashTable* ht) {
	HashIterator it;
	HashEntry* e;

	// buffer
	smark_ptr(ht->buf);

	for (e = ht_begin(&it, ht); e != NULL; e = ht_next(&it)) {
		// entry work
		smark_ptr(e);

		// no need to mark key because is symbol

		{	// mark value (SExp)
			SExp s = ptr2s(e->val);
			SMARK(s);
		}
	}
}


static int symchk(const void* key1, const void* key2) {
	const char* str1 = key1;
	const char* str2 = key2;
	return strcmp(str1, str2) == 0;
}

//=============================================================================

HashTable s_symbol_table;
static HashVTbl s_symbol_vtbl = {
	smalloc,
	sfree__,
	(unsigned int (*)(const void*))strhash,
	symchk,
};


//=============================================================================

void init_symbol_table(void) {
	ht_init(&s_symbol_table, &s_symbol_vtbl);
	ht_resize(&s_symbol_table, SymbolHashBufSize);
}

void term_symbol_table(void) {
	ht_term(&s_symbol_table);
}

SExp intern(const char* str) {
	HashEntry* e = ht_find(&s_symbol_table, str);
	if (e != NULL) {
		return ptr2s(e->val);
	} else {
		Symbol* p = new_symbol(str);
		SExp s = ptr2s(p);
		push_stack(s);
		ht_add_direct(&s_symbol_table, p->str, p);
		pop_stack(1);
		return s;
	}
}

SExp gensym(void) {
	static int no;
	char buf[16];
	sprintf(buf, "#:G%d", ++no);
	return ptr2s(new_symbol(buf));
}


//=============================================================================

void mark_symbol_table(void) {
	ht_mark(&s_symbol_table);
}
